From eee20c06fb2f12b5aa787d5ce49108a08c97256e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 12 Jul 2026 12:43:31 +0000 Subject: [PATCH] chore: version packages --- .changeset/animation-groups.md | 43 --------- .changeset/dedupe-effect-version.md | 4 - .changeset/deprecate-animation-helpers.md | 13 --- .changeset/each-intro-flag.md | 37 -------- .changeset/fix-animation-serialization.md | 9 -- .changeset/typecheck-tests.md | 5 - apps/docs/CHANGELOG.md | 12 +++ apps/docs/package.json | 2 +- packages/dom/CHANGELOG.md | 107 ++++++++++++++++++++++ packages/dom/package.json | 2 +- packages/router/CHANGELOG.md | 11 +++ packages/router/package.json | 2 +- 12 files changed, 133 insertions(+), 114 deletions(-) delete mode 100644 .changeset/animation-groups.md delete mode 100644 .changeset/dedupe-effect-version.md delete mode 100644 .changeset/deprecate-animation-helpers.md delete mode 100644 .changeset/each-intro-flag.md delete mode 100644 .changeset/fix-animation-serialization.md delete mode 100644 .changeset/typecheck-tests.md diff --git a/.changeset/animation-groups.md b/.changeset/animation-groups.md deleted file mode 100644 index 81919e6..0000000 --- a/.changeset/animation-groups.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -"@effex/dom": minor ---- - -Add animation groups — declarative sequencing across multiple animated blocks. Solves the "chained word-by-word intro" case where each word is its own `each` and word N should only start after word N-1 finishes. - -```ts -import { $, collect, each, stagger, Animation } from "@effex/dom"; - -const App = () => - Effect.gen(function* () { - const [greeting, name, tagline] = yield* Animation.sequence(3); - return $.div( - {}, - collect( - each(greetingLetters, { - key: (l) => l.id, - render: (l) => $.span({}, $.of(l.char)), - animate: { enter: "letter-in", stagger: stagger(40), group: greeting }, - }), - each(nameLetters, { - key: (l) => l.id, - render: (l) => $.span({}, $.of(l.char)), - animate: { enter: "letter-in", stagger: stagger(40), group: name }, - }), - each(taglineLetters, { - key: (l) => l.id, - render: (l) => $.span({}, $.of(l.char)), - animate: { enter: "letter-in", stagger: stagger(40), group: tagline }, - }), - ), - ); - }); -``` - -New API: - -- `Animation.group()` — creates a group with a gate (unresolved) and a completion signal. -- `Animation.sequence(count)` — returns `count` groups wired end-to-end: group 0's gate is open immediately, group N's gate opens when group N-1's registered animations all complete. -- `Animation.parallel(count)` — returns `count` groups with all gates open (useful nested inside `sequence` for concurrent segments in a follow-up release). -- `animate.group: AnimationGroup` — new option on any animated control (`each`, `when`, `match`, ...). The animation registers with the group synchronously, awaits the gate before starting, and signals completion when finished. - -Groups finalize when their pending count returns to zero for the first time after having been non-zero. Late registrations that arrive after finalization run immediately (gate is already open) — intended semantics for one-shot intros where post-sequence additions behave like ordinary animations. diff --git a/.changeset/dedupe-effect-version.md b/.changeset/dedupe-effect-version.md deleted file mode 100644 index e9e5cbc..0000000 --- a/.changeset/dedupe-effect-version.md +++ /dev/null @@ -1,4 +0,0 @@ ---- ---- - -Deduplicate `effect` dependency via a workspace-level pnpm override pinned to `3.19.19`. Previously `@effect/platform`'s peer resolved to `3.19.13` and `@effect/vitest`'s peer resolved to `3.19.19`, causing `pnpm` to install both copies. At test time the `@effect/vitest` Runtime (3.19.19) ended up executing Effects produced by code that had imported the 3.19.13 copy through `@effect/platform`, printing hundreds of "Executing an Effect versioned X with a Runtime of version Y — you may want to dedupe" warnings. Both peer ranges (`^3.0.0`) already accept 3.19.19, so a single override is sufficient. diff --git a/.changeset/deprecate-animation-helpers.md b/.changeset/deprecate-animation-helpers.md deleted file mode 100644 index d590a19..0000000 --- a/.changeset/deprecate-animation-helpers.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@effex/dom": patch ---- - -Deprecate five under-used animation helpers with `@deprecated` JSDoc tags. All continue to work — they'll be removed in a future major. - -- `staggerEased` — compose your own: `(index, total) => easingFn(index / (total - 1)) * totalDurationMs` -- `delay` — use `Effect.delay(effect, ms)` directly -- `sequence` — use `Effect.all([...], { concurrency: 1 })` directly -- `parallel` — use `Effect.all([...], { concurrency: "unbounded" })` directly -- `calculateStaggerDelay` — was only ever an internal helper; not re-exported by anything downstream - -These wrapped effect combinators without adding real value beyond a slightly shorter name; the framework should be a thin layer over Effect rather than shadow its stdlib. diff --git a/.changeset/each-intro-flag.md b/.changeset/each-intro-flag.md deleted file mode 100644 index d1e97ed..0000000 --- a/.changeset/each-intro-flag.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -"@effex/dom": minor ---- - -Add an `intro?: boolean` flag on `each` — and symmetrically on `when`, `match`, `matchOption`, `matchEither`, and `redraw` — to opt into re-animating SSR/SSG-rendered content during hydration. - -Default behaviour stays as-is: hydration attaches handlers to pre-existing DOM without re-running enter animations — the right choice for content lists (feeds, sidebars, todos) that shouldn't jitter into view on every page load. Setting `intro: true` flips that for decorative sequences where the animation *is* the point: - -```ts -each(letters, { - key: (l) => l.id, - render: (l) => $.span({}, $.of(Readable.map(l, (v) => v.char))), - animate: { - enterFrom: "opacity-0 translate-y-4", - enter: "opacity-100 translate-y-0 transition duration-300", - stagger: stagger(40), - }, - intro: true, -}); -``` - -The same flag makes sense on single-slot controls too — a hero fade-in for a `when`-gated banner, or an animated card for a `match`-selected state: - -```ts -when(isReady, { - onTrue: () => Hero(), - onFalse: () => Placeholder(), - animate: { enterFrom: "opacity-0", enter: "opacity-100 transition duration-500" }, - intro: true, -}); -``` - -On the client, `intro` is a no-op — animations already fire normally when there's no pre-existing DOM. It only affects the hydration path. - -**FOUC caveat.** Between the SSR paint and hydration applying the `enterFrom` state, there's a brief visual flash of the final state. To eliminate it, hide the container in CSS until hydration completes (e.g. `visibility: hidden` on a class you toggle from your client entry). A first-class FOUC-prevention mechanism is planned for a follow-up. - -Respects `prefers-reduced-motion` via `runEnterAnimation`. diff --git a/.changeset/fix-animation-serialization.md b/.changeset/fix-animation-serialization.md deleted file mode 100644 index 779c1a3..0000000 --- a/.changeset/fix-animation-serialization.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effex/dom": patch ---- - -Fix inline animation serialization in `addSlot` / `removeSlot`. Previously each enter/exit animation was awaited via `yield*`, which serialized the `reconcile` sync loop — for a list of `[a, b, c]` added at once, item `b`'s animation would only start after item `a`'s finished. Made `stagger` configs effectively double-counted (each addSlot's stagger delay applied *after* the previous animation completed rather than concurrently). - -Now enter animations are forked into the slot's scope so multiple slots animate concurrently, and exit animations run in a fiber attached to the parent scope so `removeSlot` returns immediately (letting the reconcile loop continue) while the exit still plays before the DOM node is removed. If a slot is removed mid-enter, closing the slot scope interrupts the enter animation before exit starts. This applies to `ClientControlCtx` and both factories in `HydrationControlCtx`. - -`@effex/router` will receive an automatic patch via `updateInternalDependencies` since it depends on `@effex/dom` as a workspace dependency. diff --git a/.changeset/typecheck-tests.md b/.changeset/typecheck-tests.md deleted file mode 100644 index 544e388..0000000 --- a/.changeset/typecheck-tests.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effex/dom": patch ---- - -Fix the `renderToString` return-type inference so provided dependencies (`RendererContext`, `ControlCtx`, `SuspenseBoundaryCtx`, `Scope`) are properly subtracted from the output `R`. Previously the signature used `Deps | R`, which TypeScript can't do set-subtraction from, so those tags leaked into the returned Effect's requirements — callers whose element required `Scope` (from `Signal.make`) or the other provided tags saw them appear in `Effect.runPromise` arguments even though `renderToString` itself provides them internally. Switched to `Exclude`; the runtime behaviour is unchanged. diff --git a/apps/docs/CHANGELOG.md b/apps/docs/CHANGELOG.md index 182e838..5a835ec 100644 --- a/apps/docs/CHANGELOG.md +++ b/apps/docs/CHANGELOG.md @@ -1,5 +1,17 @@ # docs +## 0.0.14 + +### Patch Changes + +- Updated dependencies [b650fd8] +- Updated dependencies [12654be] +- Updated dependencies [ee8a4d1] +- Updated dependencies [0dd6440] +- Updated dependencies [3c5da0c] + - @effex/dom@1.2.0 + - @effex/router@1.2.2 + ## 0.0.13 ### Patch Changes diff --git a/apps/docs/package.json b/apps/docs/package.json index c9c8949..1a5b9c6 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.0.13", + "version": "0.0.14", "private": true, "type": "module", "scripts": { diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md index 0a79532..12c1b36 100644 --- a/packages/dom/CHANGELOG.md +++ b/packages/dom/CHANGELOG.md @@ -1,5 +1,112 @@ # @effex/dom +## 1.2.0 + +### Minor Changes + +- b650fd8: Add animation groups — declarative sequencing across multiple animated blocks. Solves the "chained word-by-word intro" case where each word is its own `each` and word N should only start after word N-1 finishes. + + ```ts + import { $, Animation, collect, each, stagger } from "@effex/dom"; + + const App = () => + Effect.gen(function* () { + const [greeting, name, tagline] = yield* Animation.sequence(3); + return $.div( + {}, + collect( + each(greetingLetters, { + key: (l) => l.id, + render: (l) => $.span({}, $.of(l.char)), + animate: { + enter: "letter-in", + stagger: stagger(40), + group: greeting, + }, + }), + each(nameLetters, { + key: (l) => l.id, + render: (l) => $.span({}, $.of(l.char)), + animate: { enter: "letter-in", stagger: stagger(40), group: name }, + }), + each(taglineLetters, { + key: (l) => l.id, + render: (l) => $.span({}, $.of(l.char)), + animate: { + enter: "letter-in", + stagger: stagger(40), + group: tagline, + }, + }), + ), + ); + }); + ``` + + New API: + - `Animation.group()` — creates a group with a gate (unresolved) and a completion signal. + - `Animation.sequence(count)` — returns `count` groups wired end-to-end: group 0's gate is open immediately, group N's gate opens when group N-1's registered animations all complete. + - `Animation.parallel(count)` — returns `count` groups with all gates open (useful nested inside `sequence` for concurrent segments in a follow-up release). + - `animate.group: AnimationGroup` — new option on any animated control (`each`, `when`, `match`, ...). The animation registers with the group synchronously, awaits the gate before starting, and signals completion when finished. + + Groups finalize when their pending count returns to zero for the first time after having been non-zero. Late registrations that arrive after finalization run immediately (gate is already open) — intended semantics for one-shot intros where post-sequence additions behave like ordinary animations. + +- ee8a4d1: Add an `intro?: boolean` flag on `each` — and symmetrically on `when`, `match`, `matchOption`, `matchEither`, and `redraw` — to opt into re-animating SSR/SSG-rendered content during hydration. + + Default behaviour stays as-is: hydration attaches handlers to pre-existing DOM without re-running enter animations — the right choice for content lists (feeds, sidebars, todos) that shouldn't jitter into view on every page load. Setting `intro: true` flips that for decorative sequences where the animation _is_ the point: + + ```ts + each(letters, { + key: (l) => l.id, + render: (l) => $.span({}, $.of(Readable.map(l, (v) => v.char))), + animate: { + enterFrom: "opacity-0 translate-y-4", + enter: "opacity-100 translate-y-0 transition duration-300", + stagger: stagger(40), + }, + intro: true, + }); + ``` + + The same flag makes sense on single-slot controls too — a hero fade-in for a `when`-gated banner, or an animated card for a `match`-selected state: + + ```ts + when(isReady, { + onTrue: () => Hero(), + onFalse: () => Placeholder(), + animate: { + enterFrom: "opacity-0", + enter: "opacity-100 transition duration-500", + }, + intro: true, + }); + ``` + + On the client, `intro` is a no-op — animations already fire normally when there's no pre-existing DOM. It only affects the hydration path. + + **FOUC caveat.** Between the SSR paint and hydration applying the `enterFrom` state, there's a brief visual flash of the final state. To eliminate it, hide the container in CSS until hydration completes (e.g. `visibility: hidden` on a class you toggle from your client entry). A first-class FOUC-prevention mechanism is planned for a follow-up. + + Respects `prefers-reduced-motion` via `runEnterAnimation`. + +### Patch Changes + +- 12654be: Deprecate five under-used animation helpers with `@deprecated` JSDoc tags. All continue to work — they'll be removed in a future major. + - `staggerEased` — compose your own: `(index, total) => easingFn(index / (total - 1)) * totalDurationMs` + - `delay` — use `Effect.delay(effect, ms)` directly + - `sequence` — use `Effect.all([...], { concurrency: 1 })` directly + - `parallel` — use `Effect.all([...], { concurrency: "unbounded" })` directly + - `calculateStaggerDelay` — was only ever an internal helper; not re-exported by anything downstream + + These wrapped effect combinators without adding real value beyond a slightly shorter name; the framework should be a thin layer over Effect rather than shadow its stdlib. + +- 0dd6440: Fix inline animation serialization in `addSlot` / `removeSlot`. Previously each enter/exit animation was awaited via `yield*`, which serialized the `reconcile` sync loop — for a list of `[a, b, c]` added at once, item `b`'s animation would only start after item `a`'s finished. Made `stagger` configs effectively double-counted (each addSlot's stagger delay applied _after_ the previous animation completed rather than concurrently). + + Now enter animations are forked into the slot's scope so multiple slots animate concurrently, and exit animations run in a fiber attached to the parent scope so `removeSlot` returns immediately (letting the reconcile loop continue) while the exit still plays before the DOM node is removed. If a slot is removed mid-enter, closing the slot scope interrupts the enter animation before exit starts. This applies to `ClientControlCtx` and both factories in `HydrationControlCtx`. + + `@effex/router` will receive an automatic patch via `updateInternalDependencies` since it depends on `@effex/dom` as a workspace dependency. + +- 3c5da0c: Fix the `renderToString` return-type inference so provided dependencies (`RendererContext`, `ControlCtx`, `SuspenseBoundaryCtx`, `Scope`) are properly subtracted from the output `R`. Previously the signature used `Deps | R`, which TypeScript can't do set-subtraction from, so those tags leaked into the returned Effect's requirements — callers whose element required `Scope` (from `Signal.make`) or the other provided tags saw them appear in `Effect.runPromise` arguments even though `renderToString` itself provides them internally. Switched to `Exclude`; the runtime behaviour is unchanged. + ## 1.1.1 ### Patch Changes diff --git a/packages/dom/package.json b/packages/dom/package.json index e41dc98..f742d82 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@effex/dom", - "version": "1.1.1", + "version": "1.2.0", "description": "DOM rendering for Effex - a reactive UI framework built on Effect.ts", "type": "module", "license": "MIT", diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index b763905..546c78d 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -1,5 +1,16 @@ # @effex/router +## 1.2.2 + +### Patch Changes + +- Updated dependencies [b650fd8] +- Updated dependencies [12654be] +- Updated dependencies [ee8a4d1] +- Updated dependencies [0dd6440] +- Updated dependencies [3c5da0c] + - @effex/dom@1.2.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/router/package.json b/packages/router/package.json index e310347..c5114e1 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@effex/router", - "version": "1.2.1", + "version": "1.2.2", "description": "Router for Effex applications", "type": "module", "license": "MIT",