fix(contracts): catalogue-guards census — 12 -> 3 failures (knob/menu-dial/onion-menu + unmasked debt)
The "3 ajenos" census. What the 12 pre-existing failures really were: a few
genuine violations + several guards CRASHING on menu-dial's missing
exports.ts (ENOENT) and hiding their true violation lists. All addressed
except the two runtime-discipline files (see tail).
- knob: soma barrel export + the missing soma README (headless contract —
parts/ARIA/absolute angular keyboard/props/published CSS vars/sema table).
- menu-dial + onion-menu: joined SOMA_INTERNAL_PRIMITIVES — both are
documented provider-less bridges (keyboard dispatch + nav maths + emission;
the eidos root owns state and render). The scope guard now subtracts the
internal set from BOTH sides so a bridge may truthfully keep 'soma' in its
morfo scope without being a public namespace. Self-alias imports fixed;
the two morfo docblocks realigned to the implementation ("soma owns open"
was drift — the bridge files own dispatch/emission only; onion-menu's
stale "family-default, no pack" tail also corrected, S3b shipped its pack).
- Public barrels decoupled from provider implementation filenames (the
picker `internals.ts` house pattern) for the 6 offenders: calendar,
cropper, field-langs, gradient-builder, gradient-picker, image-adjustments.
- listSourceFiles now skips ACTIVE_DEV_TRACK paths (words/palabras/chronos)
— the track doctrine already said catalogue guards skip them; the
file-scanning guards just didn't.
- data-archetype added to the known morfo attrs (compiler-emitted from part
archetype declarations); the four field-overlay picker triggers
(color-picker, date-range-picker, time-picker, time-range-picker) declared
archetype 'trigger' while stamping 'field-trigger' at runtime — morfos
aligned to the date-picker/ntp precedent (contract now matches the DOM;
zero runtime change, the stamp already won).
- float-panel: the consumer-placed data-float-panel-no-drag opt-out marker
is now declared in the morfo (presence entry with the consumer-marker
note) — the NON_DRAG_SELECTOR contract stops being invisible.
Remaining tail (3 failures, deliberately deferred to a dedicated
runtime-discipline session with browser gesture verification):
float-panel-provider direct style writes + raw timer (drag/resize hot path
-> dom.apply/uix.timers migration) and media-player media-provider raw
listeners (-> dom.listen).
Gates: contracts 34/37 (was 25/37) · component-audit 102 PASS · vocab exit 0
· type baseline 57 · eidos-lint invalid 0 on the four touched pickers ·
compile + eidos contract tests 42/42.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
menubar-v4-safe
parent
cbd86c35a6
commit
cb554ae2e7
@ -0,0 +1,6 @@
|
||||
// Indirection layer for the public barrel.
|
||||
//
|
||||
// Soma's `exports.ts` must not re-export provider implementation filenames
|
||||
// directly. This plain-`.ts` module is the canonical re-export point —
|
||||
// consumers reach it through `$soma/components/calendar` via `exports.ts`.
|
||||
export { CalendarProvider } from './calendar-provider.svelte';
|
||||
@ -0,0 +1,6 @@
|
||||
// Indirection layer for the public barrel.
|
||||
//
|
||||
// Soma's `exports.ts` must not re-export provider implementation filenames
|
||||
// directly. This plain-`.ts` module is the canonical re-export point —
|
||||
// consumers reach it through `$soma/components/cropper` via `exports.ts`.
|
||||
export { moveRect, resizeRect } from './cropper-provider.svelte';
|
||||
@ -0,0 +1,6 @@
|
||||
// Indirection layer for the public barrel.
|
||||
//
|
||||
// Soma's `exports.ts` must not re-export provider implementation filenames
|
||||
// directly. This plain-`.ts` module is the canonical re-export point —
|
||||
// consumers reach it through `$soma/components/field-langs` via `exports.ts`.
|
||||
export { FieldLangsProvider } from './field-langs-provider.svelte';
|
||||
@ -0,0 +1,10 @@
|
||||
// Indirection layer for the public barrel.
|
||||
//
|
||||
// Soma's `exports.ts` must not re-export provider implementation filenames
|
||||
// directly. This plain-`.ts` module is the canonical re-export point —
|
||||
// consumers reach it through `$soma/components/gradient-builder` via `exports.ts`.
|
||||
export {
|
||||
GradientBuilderProvider,
|
||||
GradientStopProvider,
|
||||
GradientTrackProvider
|
||||
} from './gradient-builder-provider.svelte';
|
||||
@ -0,0 +1,11 @@
|
||||
// Indirection layer for the public barrel.
|
||||
//
|
||||
// Soma's `exports.ts` must not re-export provider implementation filenames
|
||||
// directly. This plain-`.ts` module is the canonical re-export point —
|
||||
// consumers reach it through `$soma/components/gradient-picker` via `exports.ts`.
|
||||
export {
|
||||
GradientPickerProvider,
|
||||
GradientPickerTriggerProvider,
|
||||
GradientPickerValueSwatchProvider,
|
||||
DEFAULT_PICKER_GRADIENT
|
||||
} from './gradient-picker-provider.svelte';
|
||||
@ -0,0 +1,10 @@
|
||||
// Indirection layer for the public barrel.
|
||||
//
|
||||
// Soma's `exports.ts` must not re-export provider implementation filenames
|
||||
// directly. This plain-`.ts` module is the canonical re-export point —
|
||||
// consumers reach it through `$soma/components/image-adjustments` via `exports.ts`.
|
||||
export {
|
||||
IMAGE_ADJUSTMENTS,
|
||||
DEFAULT_IMAGE_ADJUSTMENTS,
|
||||
computeImageFilter
|
||||
} from './image-adjustments-provider.svelte';
|
||||
@ -0,0 +1,97 @@
|
||||
# Knob (soma)
|
||||
|
||||
Headless rotary value control — the WAI-ARIA **slider** pattern with radial
|
||||
rendering. Continuous-shape like the Slider: a rAF-throttled `handle-drag`
|
||||
rides the gesture, framed by `handle-pick` / `handle-drop` / `commit-set`.
|
||||
Soma owns value, angular sweep, gesture, keyboard and ARIA; the eidos wrapper
|
||||
adds the dial recipe.
|
||||
|
||||
Consumer door: [`eidos/components/knob/README.md`](../../../eidos/components/knob/README.md) ·
|
||||
Morfo: [`morfo/components/knob.ts`](../../../morfo/components/knob.ts) ·
|
||||
APG: [slider](https://www.w3.org/WAI/ARIA/apg/patterns/slider/).
|
||||
|
||||
## Anatomy
|
||||
|
||||
```svelte
|
||||
<Knob.Provider bind:value min={0} max={11} defaultValue={5}>
|
||||
<Knob.Control>
|
||||
<Knob.Indicator />
|
||||
</Knob.Control>
|
||||
<Knob.Label>Gain</Knob.Label>
|
||||
<Knob.ValueText />
|
||||
<Knob.HiddenInput name="gain" />
|
||||
</Knob.Provider>
|
||||
```
|
||||
|
||||
## Parts
|
||||
|
||||
| Part | Element | Description |
|
||||
| ------------- | ---------- | ------------------------------------------------------------------------------ |
|
||||
| `Provider` | `<div>` | Root. `data-dragging` / `data-disabled` / `data-readonly` / `data-invalid` flags + the persistent `data-intent` enum. |
|
||||
| `Control` | `<div>` | The interactive dial — `role="slider"` (focusable via tabindex), full ARIA + keyboard. Publishes the angular CSS vars. |
|
||||
| `Indicator` | `<div>` | Decorative pointer/arc (`role="presentation"`, A2.3 exception) — positioned purely by the published vars. |
|
||||
| `Label` | `<span>` | Visible label; the Control links it via `aria-labelledby` (or falls back to a translated `aria-label` when absent — `part-absent` condition). |
|
||||
| `ValueText` | `<span>` | Visible readout, `aria-hidden` (the Control already carries `aria-valuetext`). |
|
||||
| `ValueField` | `<div>` | The EDITABLE readout: composes a real `NumberField` wired to the knob's value (compound-in-component; event isolation). |
|
||||
| `HiddenInput` | `<input>` | Form bridge, composed when `name` is set (A13). |
|
||||
|
||||
## ARIA (Control)
|
||||
|
||||
`aria-valuemin` / `aria-valuemax` / `aria-valuenow` + `aria-valuetext`
|
||||
(locale-formatted by the provider via `formatValue`),
|
||||
`aria-roledescription` = translated "rotary knob", `aria-labelledby` ↔ Label,
|
||||
`aria-disabled` / `aria-readonly` / `aria-invalid` when the flags are on.
|
||||
|
||||
## Keyboard
|
||||
|
||||
Angular and ABSOLUTE — a deliberate A12 exception documented in the morfo:
|
||||
rotation does not mirror in RTL (a rotary's increase direction is angular,
|
||||
not inline).
|
||||
|
||||
| Key | Action |
|
||||
| ---------------------------- | ---------------------------------------- |
|
||||
| `ArrowUp` / `ArrowRight` | Increase by `step` |
|
||||
| `ArrowDown` / `ArrowLeft` | Decrease by `step` |
|
||||
| `PageUp` / `PageDown` | Increase / decrease by `largeStep` |
|
||||
| `Home` / `End` | Jump to `min` / `max` |
|
||||
| double-click | Reset to `defaultValue` (audio-console convention → `commit-reset`) |
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Default | Notes |
|
||||
| ----------------------- | -------------------------------------- | ---------- | -------------------------------------------------------- |
|
||||
| `value` | `number` | — | Bindable. |
|
||||
| `onValueChange` / `onValueCommit` | `OnChangeFn<number>` | — | Continuous / terminal. |
|
||||
| `min` / `max` / `step` / `largeStep` | `number` | — | Range + quantisation. |
|
||||
| `defaultValue` | `number` | — | The double-click reset anchor (knob KEEPS the prop — reasoned N8 exception). |
|
||||
| `startAngle` / `endAngle` | `number` | — | The angular sweep. |
|
||||
| `formatValue` | `(value: number) => string` | — | Feeds the localized `aria-valuetext`. |
|
||||
| `intent` | `'neutral' \| 'affirm' \| 'risk' \| 'threat'` | `'neutral'` | Persistent evaluative state (`data-intent`) AND the `commit-set` intent via `fromProp` — a gain knob past 0dB can commit with `risk`. |
|
||||
| `disabled` / `readonly` / `invalid` | `boolean` | `false` | Flags. |
|
||||
| `name` | `string` | — | Renders the HiddenInput form bridge. |
|
||||
|
||||
## Published CSS variables
|
||||
|
||||
The provider inline-styles the Control with the angular geometry every frame
|
||||
of the gesture — the recipe consumes them and never computes geometry from
|
||||
layout reads:
|
||||
|
||||
`--knob-progress` (0…1) · `--knob-angle` (deg) · `--knob-start-angle` ·
|
||||
`--knob-sweep`.
|
||||
|
||||
## Sema events
|
||||
|
||||
Pack: `sema/components/knob.ts` (`expression: 'pack'`).
|
||||
|
||||
| Name | Family | Verb | Sequence | Intent | Notes |
|
||||
| -------------- | -------- | ------ | ------------ | ----------------- | ------------------------------------------------------------ |
|
||||
| `handle-pick` | `handle` | `pick` | `post` | — | Gesture engages; pointer capture is not gated behind the hold (checkbox-lag doctrine). |
|
||||
| `handle-drag` | `handle` | `drag` | `coincident` | — | rAF-throttled, per-emit payload (progress + angular velocity) — the Slider mirror. |
|
||||
| `handle-drop` | `handle` | `drop` | `pre` | — | The release moment. The only `drop` declared in the handle census. |
|
||||
| `commit-set` | `commit` | `set` | `post` | `fromProp` intent | Pointer release with a changed value AND every discrete keyboard step (A-3.7). |
|
||||
| `commit-reset` | `commit` | `reset`| `post` | `neutral` | Double-click → `defaultValue`. |
|
||||
|
||||
## Suite
|
||||
|
||||
`knob-provider.svelte.test.ts` — the provider behaviour suite (value flow,
|
||||
keyboard steps, gesture cycle).
|
||||
Loading…
Reference in new issue