From 9f007a84f7e1301146b83900737d84be2bc7e9f5 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 5 Jun 2026 04:55:56 +0200 Subject: [PATCH] refactor(uix): move useId counter from soma to $active-uix/id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The soma `id` module conflated two concerns: `createId` (wraps Svelte `$props.id()` for SSR/ARIA element ids — ~200 soma importers, intrinsic to the headless layer) and `useId` (a generic monotonic counter — 6 importers). Only the counter is a cross-layer utility. Move `useId` to a new `$active-uix/id` subpath (mirroring the existing `$active-uix/prefs` that soma already imports), so any UIX layer — soma, eidos, demos, apps — can mint client-only ids from a cheap, collision-free counter without coupling to soma. `createId` stays in soma (its domain). + src/uix/active-uix/id/index.ts (useId) ~ soma/id now exports createId only ~ 6 useId importers repointed to $active-uix/id (floating, toaster, date-field, time-field, accordion-item, internal/arrow) Output format unchanged (`soma-{component}-{n}`) -> zero behavior change. `npm run check` 0 errors; toast/accordion/popover verified in browser. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/uix/active-uix/id/index.ts | 24 +++++++++++++++++++ src/uix/soma/SOMA_ARCHITECTURE.md | 2 +- .../components/accordion-item.svelte | 3 ++- .../date-field/date-field-provider.svelte.ts | 2 +- src/uix/soma/components/internal/arrow.svelte | 2 +- .../time-field/time-field-provider.svelte.ts | 2 +- .../soma/components/toast/toaster.svelte.ts | 2 +- src/uix/soma/id/create-id.ts | 12 ++++------ src/uix/soma/id/index.ts | 2 +- .../soma/layers/floating/floating.svelte.ts | 2 +- 10 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 src/uix/active-uix/id/index.ts diff --git a/src/uix/active-uix/id/index.ts b/src/uix/active-uix/id/index.ts new file mode 100644 index 000000000..4c8e40f3b --- /dev/null +++ b/src/uix/active-uix/id/index.ts @@ -0,0 +1,24 @@ +/** + * Process-unique client-side id counter. + * + * `useId()` returns the next value of a monotonic module-level counter — + * cheaper than `Date.now()` and, unlike a timestamp, collision-free. Use it + * for client-only ephemeral ids (list keys, generated handles). + * + * NOT for SSR / ARIA element ids derived from the component tree — those use + * soma's `createId`, which wraps Svelte's `$props.id()` for hydration-stable + * output. A global counter would desync between server and client renders. + * + * Lives in active-uix (not soma) so any UIX layer — soma, eidos, demos, + * apps — can mint ids without coupling to soma. Imported by subpath + * (`$active-uix/id`), mirroring `$active-uix/prefs`, so it never pulls the + * runtime barrel. The `soma-` prefix is the shared UIX id convention (same + * shape `createId` emits), not a marker of where this lives. + */ + +let counter = 0 + +export function useId(component = ''): string { + const id = ++counter + return component ? `soma-${component}-${id}` : `soma-${id}` +} diff --git a/src/uix/soma/SOMA_ARCHITECTURE.md b/src/uix/soma/SOMA_ARCHITECTURE.md index 0b39ea915..74bdc1ff8 100644 --- a/src/uix/soma/SOMA_ARCHITECTURE.md +++ b/src/uix/soma/SOMA_ARCHITECTURE.md @@ -838,7 +838,7 @@ src/uix/soma/ ├── keyboard/ ← KEYS, directional ├── dom/ ← DOM utilities, focus ├── css/ ← styleToString, cssToStyleObj -├── id/ ← createId, useId +├── id/ ← createId (useId → $active-uix/id) ├── types/ ← shared types + service interfaces ├── layers/ ← behavior layers (classes only) │ ├── presence.svelte.ts diff --git a/src/uix/soma/components/accordion/components/accordion-item.svelte b/src/uix/soma/components/accordion/components/accordion-item.svelte index ccc245efd..99e6068f4 100644 --- a/src/uix/soma/components/accordion/components/accordion-item.svelte +++ b/src/uix/soma/components/accordion/components/accordion-item.svelte @@ -4,7 +4,8 @@ writableActive } from '$libs/reactive'; import { mergeProps } from '../../../props'; - import { createId, useId } from '../../../id'; + import { createId } from '../../../id'; + import { useId } from '$active-uix/id'; import { AccordionItemProvider } from '../accordion-provider.svelte'; import type { AccordionItemProps } from '../types'; diff --git a/src/uix/soma/components/date-field/date-field-provider.svelte.ts b/src/uix/soma/components/date-field/date-field-provider.svelte.ts index 5422a5455..c6672c7f3 100644 --- a/src/uix/soma/components/date-field/date-field-provider.svelte.ts +++ b/src/uix/soma/components/date-field/date-field-provider.svelte.ts @@ -21,7 +21,7 @@ import type { } from '../../types'; import { KEYS, getDirectionalKeys } from '../../keyboard'; import { isNumberString } from '../../types/guards'; -import { useId } from '../../id'; +import { useId } from '$active-uix/id'; import { Soma } from '../../core/soma.svelte'; import { DateFormatter, diff --git a/src/uix/soma/components/internal/arrow.svelte b/src/uix/soma/components/internal/arrow.svelte index 41c6d91ca..23ab414c7 100644 --- a/src/uix/soma/components/internal/arrow.svelte +++ b/src/uix/soma/components/internal/arrow.svelte @@ -1,6 +1,6 @@