From bbfefdb63d2fb08e3c7be2c92f29594f9c954023 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 25 May 2026 18:41:19 +0200 Subject: [PATCH] refactor(morfo): rename 12 events to {family}-{verb}-{variant} canonical shape (Capa 3 cluster otros) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the last cluster of name-shape drift in the non-words morfos. 11 are pure cosmetic renames (declared verb already canonical); 1 includes a doctrinal correction. Renames (verbo declarado canónico, solo el name): - carousel.shift-slide → shift-navigate-slide - feed.shift-focus-item → shift-navigate-focus-item - feed.commit-load-more → commit-submit-load-more - file-upload.commit-add → commit-set-add - file-upload.signal-reject → signal-warn-reject - form.signal-invalid → signal-warn-invalid - number-field.handle-scrub → handle-drag-scrub - range-calendar.commit-start → commit-select-start - range-calendar.commit-range → commit-select-range - tags-input.commit-add → commit-set-add - tags-input.signal-reject → signal-warn-reject Doctrinal correction: - command.commit-invoke (declared submit + fulfill) → commit-submit-invoke + submit + AFFIRM (not fulfill) Per book cap. 22 §8 ("Error típico: contact.press + fulfill — esto hace que el sistema diga 'ya terminó' cuando solo ha empezado. Contact inicia. Commit resuelve."), the user submitting a command in the palette does NOT mean the command has completed. Same precedent as Button: celebrate-before-time is wrong. Affirm acknowledges the submission; the command's actual completion (success/failure) fires downstream from whichever morfo owns it. Consumers updated: - sema/components/{file-upload,form,number-field,tags-input}.ts - soma/components/{file-upload,form,number-field,range-calendar,tags-input}/*.ts - sema/resolver.test.ts (event name + data-event selector) Verification: - morfo:vocabulary: warns 19 → 7 (-12), the 7 remaining are all words.* (separate dev track, excluded). EXIT 0. - vitest src/uix/sema src/uix/morfo: 195/195 pass Doc src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md C.3 marked RESUELTO with full list of renames + the command intent correction. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md | 31 ++++++++++--------- src/uix/morfo/components/carousel.ts | 4 ++- src/uix/morfo/components/command.ts | 9 ++++-- src/uix/morfo/components/feed.ts | 10 ++++-- src/uix/morfo/components/file-upload.ts | 4 +-- src/uix/morfo/components/form.ts | 2 +- src/uix/morfo/components/number-field.ts | 2 +- src/uix/morfo/components/range-calendar.ts | 4 +-- src/uix/morfo/components/tags-input.ts | 4 +-- src/uix/sema/components/file-upload.ts | 4 +-- src/uix/sema/components/form.ts | 2 +- src/uix/sema/components/number-field.ts | 2 +- src/uix/sema/components/tags-input.ts | 4 +-- src/uix/sema/resolver.test.ts | 8 ++--- .../file-upload-provider.svelte.ts | 4 +-- .../components/form/form-provider.svelte.ts | 2 +- .../number-field-provider.svelte.ts | 2 +- .../range-calendar-provider.svelte.ts | 4 +-- .../tags-input/tags-input-provider.svelte.ts | 12 +++---- 19 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md b/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md index 2867de8e9..da81c6ea6 100644 --- a/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md +++ b/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md @@ -203,20 +203,23 @@ Hoy ~35 events siguen como warns en `morfo:vocabulary` porque el verbo declarado - **Name**: `commit-unselect` se mantiene (el name ya parsea canónico ahora que `unselect` está en `SEMA_VERBS.commit`). - **Para el libro**: añadir `unselect` a la lista de verbos canónicos de `commit` en Cap 23 §5. El par `select`/`unselect` es el natural sobre estado de selección de items. -### C.3 Otros nombres no canónicos (~19 events individuales) - -Eventos con declaración canónica pero name no calza con `{family}-{verb}` (warns lint): -- `carousel.shift-slide` (declared `navigate`) — slide change, escala intra-componente -- `command.commit-invoke` (declared `submit`) — invocar comando del palette -- `file-upload.commit-add` (declared `set`) — añadir archivos -- `file-upload.signal-reject` (declared `warn`) — rechazar archivo inválido -- `form.signal-invalid` (declared `warn`) — validación fallida -- `number-field.handle-scrub` (declared `drag`) — gesto scrub del number field -- `range-calendar.commit-start` (declared `select`) — primer endpoint del rango -- `range-calendar.commit-range` (declared `select`) — rango completo -- (... y otros) - -La mayoría son renames cosméticos. Pero algunos esconden preguntas doctrinales (¿es `command.commit-invoke` realmente `commit.submit` o `commit.select`?). +### C.3 Otros nombres no canónicos — RESUELTO + +- **Status**: Cerrado 2026-05-25. 12 events renombrados a `{family}-{verb}-{variant}` canónico. +- **Renames mecánicos** (verbo declarado canónico, solo name): + - `carousel.shift-slide` → `shift-navigate-slide` + - `feed.shift-focus-item` → `shift-navigate-focus-item` + - `feed.commit-load-more` → `commit-submit-load-more` + - `file-upload.commit-add` → `commit-set-add` + - `file-upload.signal-reject` → `signal-warn-reject` + - `form.signal-invalid` → `signal-warn-invalid` + - `number-field.handle-scrub` → `handle-drag-scrub` + - `range-calendar.commit-start` → `commit-select-start` + - `range-calendar.commit-range` → `commit-select-range` + - `tags-input.commit-add` → `commit-set-add` + - `tags-input.signal-reject` → `signal-warn-reject` +- **Una corrección doctrinal**: `command.commit-invoke` (declared `submit + fulfill`) → `commit-submit-invoke + submit + affirm`. Cambio de intent: `fulfill` → `affirm` per Cap 22 §8 — el comando aún no se ha ejecutado al click, celebrar al click es antipatrón (mismo precedent que Button). Verb `submit` mantenido (el usuario envía su elección al sistema de comandos). +- **Consumers actualizados**: sema cascades + soma providers + resolver.test.ts. --- diff --git a/src/uix/morfo/components/carousel.ts b/src/uix/morfo/components/carousel.ts index a60d2cf0b..b359d99c8 100644 --- a/src/uix/morfo/components/carousel.ts +++ b/src/uix/morfo/components/carousel.ts @@ -16,7 +16,9 @@ export const carouselMorfo = { events: [ { // Slide change — user-initiated (button/indicator) or auto-rotation. - name: 'shift-slide', + // Per book cap. 27 §5 shift.navigate ("Página A → página B"). Variant + // `slide` preserves the carousel-specific label. + name: 'shift-navigate-slide', semantic: { family: 'shift', verb: 'navigate', diff --git a/src/uix/morfo/components/command.ts b/src/uix/morfo/components/command.ts index 3abe4d1fd..84952cfcd 100644 --- a/src/uix/morfo/components/command.ts +++ b/src/uix/morfo/components/command.ts @@ -12,12 +12,17 @@ export const commandMorfo = { events: [ { // User invoked a command (Enter on highlighted item or click). - name: 'commit-invoke', + // Per book cap. 23 `commit.submit` — the user submitted their choice + // to the command system. Intent `affirm` (not `fulfill`) per book + // cap. 22 §8 — the command's actual outcome (success / failure / + // completion) fires downstream; celebrating at the click is + // "celebrate before time". Same precedent as Button. + name: 'commit-submit-invoke', semantic: { family: 'commit', verb: 'submit', target: v.partRef('item'), - intent: 'fulfill', + intent: 'affirm', sequence: 'post' } } diff --git a/src/uix/morfo/components/feed.ts b/src/uix/morfo/components/feed.ts index 086dd1917..00bcf748d 100644 --- a/src/uix/morfo/components/feed.ts +++ b/src/uix/morfo/components/feed.ts @@ -12,8 +12,11 @@ export const feedMorfo = { events: [ { // User asked for more items (infinite scroll trigger / load-more - // button). Soma owns whether/when to actually fetch. - name: 'commit-load-more', + // button). Soma owns whether/when to actually fetch. Per book + // cap. 23 `commit.submit` — user submitted their request. The + // actual fetch is downstream (sustain.processing → + // commit.complete + affirm or commit.fail + risk). + name: 'commit-submit-load-more', semantic: { family: 'commit', verb: 'submit', @@ -24,7 +27,8 @@ export const feedMorfo = { }, { // Item focused via keyboard navigation (j/k or Arrow keys). - name: 'shift-focus-item', + // Per book cap. 27 §5 `shift.navigate`. + name: 'shift-navigate-focus-item', semantic: { family: 'shift', verb: 'navigate', diff --git a/src/uix/morfo/components/file-upload.ts b/src/uix/morfo/components/file-upload.ts index ef7a3a8d8..62b06c9df 100644 --- a/src/uix/morfo/components/file-upload.ts +++ b/src/uix/morfo/components/file-upload.ts @@ -23,7 +23,7 @@ export const fileUploadMorfo = { } }, { - name: 'commit-add', + name: 'commit-set-add', semantic: { family: 'commit', verb: 'set', @@ -33,7 +33,7 @@ export const fileUploadMorfo = { } }, { - name: 'signal-reject', + name: 'signal-warn-reject', semantic: { family: 'signal', verb: 'warn', diff --git a/src/uix/morfo/components/form.ts b/src/uix/morfo/components/form.ts index 83cf6ddb2..28d4ad04b 100644 --- a/src/uix/morfo/components/form.ts +++ b/src/uix/morfo/components/form.ts @@ -22,7 +22,7 @@ export const formMorfo = { } }, { - name: 'signal-invalid', + name: 'signal-warn-invalid', semantic: { family: 'signal', verb: 'warn', diff --git a/src/uix/morfo/components/number-field.ts b/src/uix/morfo/components/number-field.ts index 6f4b32c0f..58c4f5b7d 100644 --- a/src/uix/morfo/components/number-field.ts +++ b/src/uix/morfo/components/number-field.ts @@ -31,7 +31,7 @@ export const numberFieldMorfo = { } }, { - name: 'handle-scrub', + name: 'handle-drag-scrub', semantic: { family: 'handle', verb: 'drag', diff --git a/src/uix/morfo/components/range-calendar.ts b/src/uix/morfo/components/range-calendar.ts index 7b9d1d6c0..d36568a24 100644 --- a/src/uix/morfo/components/range-calendar.ts +++ b/src/uix/morfo/components/range-calendar.ts @@ -14,7 +14,7 @@ export const rangeCalendarMorfo = { }, events: [ { - name: 'commit-start', + name: 'commit-select-start', semantic: { family: 'commit', verb: 'select', @@ -24,7 +24,7 @@ export const rangeCalendarMorfo = { } }, { - name: 'commit-range', + name: 'commit-select-range', semantic: { family: 'commit', verb: 'select', diff --git a/src/uix/morfo/components/tags-input.ts b/src/uix/morfo/components/tags-input.ts index 72f980b1d..0e15ccf08 100644 --- a/src/uix/morfo/components/tags-input.ts +++ b/src/uix/morfo/components/tags-input.ts @@ -12,7 +12,7 @@ export const tagsInputMorfo = { }, events: [ { - name: 'commit-add', + name: 'commit-set-add', semantic: { family: 'commit', verb: 'set', @@ -22,7 +22,7 @@ export const tagsInputMorfo = { } }, { - name: 'signal-reject', + name: 'signal-warn-reject', semantic: { family: 'signal', verb: 'warn', diff --git a/src/uix/sema/components/file-upload.ts b/src/uix/sema/components/file-upload.ts index 8cfd888ce..b08948104 100644 --- a/src/uix/sema/components/file-upload.ts +++ b/src/uix/sema/components/file-upload.ts @@ -21,12 +21,12 @@ export const fileUploadSema: Sema = { haptic: { kind: 'tick' } }, { - selector: onProvider({ eventName: 'commit-add' }), + selector: onProvider({ eventName: 'commit-set-add' }), sound: soundTuning('form.commit.soft'), haptic: { kind: 'success' } }, { - selector: onProvider({ eventName: 'signal-reject' }), + selector: onProvider({ eventName: 'signal-warn-reject' }), sound: soundTuning('form.commit.soft', { contour: 'descending', pitch: { op: 'add', value: -120 } diff --git a/src/uix/sema/components/form.ts b/src/uix/sema/components/form.ts index 56db31ab3..beeea333f 100644 --- a/src/uix/sema/components/form.ts +++ b/src/uix/sema/components/form.ts @@ -18,7 +18,7 @@ export const formSema: Sema = { haptic: { kind: 'success' } }, { - selector: onProvider({ eventName: 'signal-invalid' }), + selector: onProvider({ eventName: 'signal-warn-invalid' }), sound: sound('alert.error', { gain: 0.1 }), haptic: { kind: 'warning' } }, diff --git a/src/uix/sema/components/number-field.ts b/src/uix/sema/components/number-field.ts index c3306d991..4f8215b58 100644 --- a/src/uix/sema/components/number-field.ts +++ b/src/uix/sema/components/number-field.ts @@ -32,7 +32,7 @@ export const numberFieldSema: Sema = { haptic: { kind: 'tick', intensity: 0.18, duration: 6 } }, { - selector: onScrubber({ eventName: 'handle-scrub' }), + selector: onScrubber({ eventName: 'handle-drag-scrub' }), channels: ['haptic'], haptic: { kind: 'tick', intensity: 0.08, duration: 4 } } diff --git a/src/uix/sema/components/tags-input.ts b/src/uix/sema/components/tags-input.ts index cfa728f2f..331f583bf 100644 --- a/src/uix/sema/components/tags-input.ts +++ b/src/uix/sema/components/tags-input.ts @@ -16,12 +16,12 @@ export const tagsInputSema: Sema = { name: 'tags-input', cascade: [ { - selector: onProvider({ eventName: 'commit-add' }), + selector: onProvider({ eventName: 'commit-set-add' }), sound: soundTuning('form.commit.soft'), haptic: { kind: 'success' } }, { - selector: onInput({ eventName: 'signal-reject' }), + selector: onInput({ eventName: 'signal-warn-reject' }), sound: soundTuning('form.commit.soft', { contour: 'descending', pitch: { op: 'add', value: -120 } diff --git a/src/uix/sema/resolver.test.ts b/src/uix/sema/resolver.test.ts index a64cf705f..8cc140259 100644 --- a/src/uix/sema/resolver.test.ts +++ b/src/uix/sema/resolver.test.ts @@ -386,12 +386,12 @@ describe('resolveSignature', () => { it('file-upload rejection emits a risk warning cue', () => { const target = makeMockTarget( (sel) => - sel === '[data-file-upload][data-event="signal-reject"]' || sel === '[data-file-upload]' + sel === '[data-file-upload][data-event="signal-warn-reject"]' || sel === '[data-file-upload]' ); const eff = resolveSignature( { target, - name: 'signal-reject', + name: 'signal-warn-reject', family: 'signal', intent: 'risk' }, @@ -406,13 +406,13 @@ describe('resolveSignature', () => { it('tags-input rejection emits a risk warning cue on the input', () => { const target = makeMockTarget( (sel) => - sel === '[data-tags-input-input][data-event="signal-reject"]' || + sel === '[data-tags-input-input][data-event="signal-warn-reject"]' || sel === '[data-tags-input-input]' ); const eff = resolveSignature( { target, - name: 'signal-reject', + name: 'signal-warn-reject', family: 'signal', intent: 'risk' }, diff --git a/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts b/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts index f77803b54..38d2f6b44 100644 --- a/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts +++ b/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts @@ -186,11 +186,11 @@ export class FileUploadProvider { const next = this.opts.multiple.current ? [...current, ...toAdd] : toAdd; this.opts.files.current = next; this.opts.onFilesChange.current?.(next); - void this.runtime.trigger('commit-add', target ? { fallbackTarget: target } : undefined); + void this.runtime.trigger('commit-set-add', target ? { fallbackTarget: target } : undefined); } if (rejected.length > 0) { this.opts.onReject.current?.(rejected); - void this.runtime.trigger('signal-reject', target ? { fallbackTarget: target } : undefined); + void this.runtime.trigger('signal-warn-reject', target ? { fallbackTarget: target } : undefined); } } diff --git a/src/uix/soma/components/form/form-provider.svelte.ts b/src/uix/soma/components/form/form-provider.svelte.ts index f1d1b6871..1eaa49f97 100644 --- a/src/uix/soma/components/form/form-provider.svelte.ts +++ b/src/uix/soma/components/form/form-provider.svelte.ts @@ -124,7 +124,7 @@ export class FormProvider { const target = this.opts.ref?.current; if (!ok) { void this.runtime.trigger( - 'signal-invalid', + 'signal-warn-invalid', target ? { fallbackTarget: target } : undefined ); this.focusFirstInvalid(); diff --git a/src/uix/soma/components/number-field/number-field-provider.svelte.ts b/src/uix/soma/components/number-field/number-field-provider.svelte.ts index 908221b1f..9d8267378 100644 --- a/src/uix/soma/components/number-field/number-field-provider.svelte.ts +++ b/src/uix/soma/components/number-field/number-field-provider.svelte.ts @@ -827,7 +827,7 @@ export class NumberFieldScrubberProvider { } e.preventDefault(); - void this.provider.runtime.trigger('handle-scrub', { + void this.provider.runtime.trigger('handle-drag-scrub', { fallbackTarget: e.currentTarget as HTMLElement }); diff --git a/src/uix/soma/components/range-calendar/range-calendar-provider.svelte.ts b/src/uix/soma/components/range-calendar/range-calendar-provider.svelte.ts index 49e9b4ac4..d04b184da 100644 --- a/src/uix/soma/components/range-calendar/range-calendar-provider.svelte.ts +++ b/src/uix/soma/components/range-calendar/range-calendar-provider.svelte.ts @@ -400,7 +400,7 @@ export class RangeCalendarProvider { this.anchor = date; this.previewDate = undefined; this.opts.value.current = { start: date, end: undefined }; - void this.runtime.trigger('commit-start', target ? { fallbackTarget: target } : undefined); + void this.runtime.trigger('commit-select-start', target ? { fallbackTarget: target } : undefined); } private clearSelection(target?: HTMLElement): void { @@ -456,7 +456,7 @@ export class RangeCalendarProvider { this.opts.value.current = { start, end }; this.anchor = undefined; this.previewDate = undefined; - void this.runtime.trigger('commit-range', target ? { fallbackTarget: target } : undefined); + void this.runtime.trigger('commit-select-range', target ? { fallbackTarget: target } : undefined); } /** Called by Day on pointermove when the user is mid-range. */ diff --git a/src/uix/soma/components/tags-input/tags-input-provider.svelte.ts b/src/uix/soma/components/tags-input/tags-input-provider.svelte.ts index f6d5c221f..1d06d02f1 100644 --- a/src/uix/soma/components/tags-input/tags-input-provider.svelte.ts +++ b/src/uix/soma/components/tags-input/tags-input-provider.svelte.ts @@ -92,9 +92,9 @@ export class TagsInputProvider { private emitAddResult(result: AddTagResult, target?: HTMLElement): void { if (result === 'added') { - void this.runtime.trigger('commit-add', { fallbackTarget: target }); + void this.runtime.trigger('commit-set-add', { fallbackTarget: target }); } else if (result === 'rejected') { - void this.runtime.trigger('signal-reject', { + void this.runtime.trigger('signal-warn-reject', { fallbackTarget: target ?? this.inputRef.current ?? undefined }); } @@ -180,12 +180,12 @@ export class TagsInputProvider { if (added) { this.opts.inputValue.current = ''; - void this.runtime.trigger('commit-add', { + void this.runtime.trigger('commit-set-add', { fallbackTarget: target ?? this.inputRef.current ?? undefined }); } if (rejected) { - void this.runtime.trigger('signal-reject', { + void this.runtime.trigger('signal-warn-reject', { fallbackTarget: target ?? this.inputRef.current ?? undefined }); } @@ -208,12 +208,12 @@ export class TagsInputProvider { } this.opts.inputValue.current = remaining; if (added) { - void this.runtime.trigger('commit-add', { + void this.runtime.trigger('commit-set-add', { fallbackTarget: target ?? this.inputRef.current ?? undefined }); } if (rejected) { - void this.runtime.trigger('signal-reject', { + void this.runtime.trigger('signal-warn-reject', { fallbackTarget: target ?? this.inputRef.current ?? undefined }); }