Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
9852cc5
feat(compositor): add opt-in follow and native settle
Sep 30, 2026
823094d
fix(compositor): isolate follow and verify packaged recipes
Oct 1, 2026
3c2ef46
fix(compositor): preserve input ownership and private declarations
Oct 1, 2026
1a9a31a
fix(compositor): require accepted output before retiring donor
Oct 1, 2026
827ff13
test(behaviors): avoid matcher overhead in cascade model
Oct 1, 2026
31b9394
fix(docs): track active pointer across document targets
Oct 1, 2026
115feea
test(compositor): trace WebKit release boundaries
Oct 1, 2026
ea9e397
test(compositor): attach pointer observer after document setup
Oct 1, 2026
42069a9
test(compositor): observe native drag boundaries
Oct 1, 2026
a792de5
fix(recipes): retain pointer ownership during native drag
Oct 1, 2026
8830e6b
test(compositor): remove temporary follow diagnostics
Oct 1, 2026
ed9e05c
test(resource): verify installed follow terminal retention
Oct 1, 2026
33f496a
Merge remote-tracking branch 'origin/main' into codex/motion-generic-…
Oct 1, 2026
5e2ff31
fix: own motion lifetimes and verify packed production paths
Oct 1, 2026
86b4595
fix: сохранить terminal-владение и причинность серверных измерений
Oct 1, 2026
30e7399
merge: актуальные React зависимости и детерминированная фикстура main
Oct 1, 2026
8fb55ca
Уточнить часы публикации серверного PROFILE и стоимость отказа
Oct 1, 2026
bec4888
perf(profile): cover default MotionValue lifecycle cost
Oct 1, 2026
6eae2d4
docs(profile): explain retention boundary in Russian
Oct 1, 2026
8f45bb2
fix(bench): загружать браузерные инструменты при выполнении
Oct 1, 2026
7f9b85d
perf(bench): сократить копирование при полном admission
Oct 1, 2026
185f02c
fix(profile): закрыть входные проверки и снизить стоимость admission
Oct 1, 2026
088fbc6
fix(profile): измерять актуальный CPU runtime потока Linux
Oct 2, 2026
4bc697c
Сжать CPU evidence без потери полей и сократить справку
Oct 2, 2026
5a4c29d
test(profile): закрепить точные CPU RLE векторы
Oct 2, 2026
6502fda
test(profile): сократить повторную подготовку полных историй
Oct 2, 2026
f6476ae
test(profile): убрать неподтверждённую оптимизацию fixture
Oct 2, 2026
80a91f5
test(profile): разделить независимые законы admission
Oct 2, 2026
95a4265
test(profile): проверять настоящий native clock в CI
Oct 2, 2026
fe09233
fix(recipes): восстановить исходный приоритет transform
Oct 2, 2026
8a97cfd
fix(motion): сохранить владельцев подписок и перезапусков
lemone112 Oct 2, 2026
939e7f4
fix(profile): сохранять отказ мощности и штатную остановку
Oct 2, 2026
c56db84
test(motion): проверять pickup из отличимой конечной позы
lemone112 Oct 2, 2026
c59e47a
fix(motion): погашать заявку кадра до входа в тик
lemone112 Oct 2, 2026
6393df5
fix(vue): release motion when its effect scope stops
Oct 2, 2026
4341f80
fix(quality): check dynamic constructor re-export imports
Oct 2, 2026
9e61ede
chore(motion): prepare isolated integration source
Oct 2, 2026
76b90e6
fix(compositor): integrate follow resource ownership
Oct 2, 2026
a9126ba
test(profile): preserve historical budget scope
Oct 2, 2026
389faa2
test(benchmark): time independent report cases separately
Oct 2, 2026
4f6bee1
test(resource): reuse observed retarget effect count
Oct 2, 2026
78e7ab2
test(profile): avoid repeated stock matcher bookkeeping
Oct 2, 2026
12f2ac7
test(journey): verify grid reorder in installed browser bundle
Oct 3, 2026
2933a42
test(journey): preserve text selection through live retarget
Oct 3, 2026
2eab62c
test(journey): distinguish pointer end from filter cancellation
Oct 3, 2026
725adc2
feat(motion): add installed-package reading shelf consumer
Oct 3, 2026
14e814c
fix(motion): restore keyboard reorder on escape and close blur session
Oct 3, 2026
dac184e
fix(motion): harden reading shelf lifecycle and package receipt
Oct 3, 2026
cdbe08e
fix(motion): rebuild packed consumer bytes from source
Oct 3, 2026
0e6b8ee
feat(motion): add installed-package itinerary sheet consumer
Oct 3, 2026
506d735
fix(motion): retain itinerary sheet snap across page return
Oct 3, 2026
7bb19d3
fix(motion): reselect sheet tier when motion preference changes
Oct 3, 2026
9f38e60
test(motion): assert sheet settles after reduced-motion exit
Oct 3, 2026
d7d727d
feat(motion): add packed card detail journey consumer
Oct 3, 2026
932ce7e
fix(motion): rebase card projection on geometry changes
Oct 4, 2026
8a083e9
test(motion): cover card geometry and return lifecycle
Oct 4, 2026
35aaa32
feat(examples): add call sheet reorder consumer
Oct 4, 2026
7cf6f6f
test(motion): bind card consumer to archived export bytes
Oct 4, 2026
dc177c1
fix(examples): keep scene controls named after edits
Oct 4, 2026
a7b22eb
fix(motion): address card consumer review findings
Oct 4, 2026
8de870a
feat(examples): add audio cue timeline consumer
Oct 4, 2026
e1564fe
test(motion): reset resize measurement at viewport change
Oct 4, 2026
4b21e06
feat(examples): add shot inspector representation consumer
Oct 4, 2026
fd4d1dd
test(examples): reject post-pack source changes
Oct 4, 2026
1f254b9
fix(examples): align cue geometry with timing
Oct 4, 2026
e31c233
fix(examples): honor final pointer goal on sheet release
Oct 4, 2026
10e7155
chore(integration): combine motion PR #462
lemone112 Oct 4, 2026
953c8fc
chore(integration): combine motion PR #463
lemone112 Oct 4, 2026
4f93744
chore(integration): combine motion PR #464
lemone112 Oct 4, 2026
3f4b57a
chore(integration): combine motion PR #465
lemone112 Oct 4, 2026
e130b67
chore(integration): combine motion PR #466
lemone112 Oct 4, 2026
1dc9fdc
chore(integration): combine motion PR #467
lemone112 Oct 4, 2026
8dd8490
chore(integration): combine motion PR #468
lemone112 Oct 4, 2026
8d39585
chore(integration): combine motion PR #469
lemone112 Oct 4, 2026
63e37e4
chore(integration): combine motion PR #470
lemone112 Oct 4, 2026
e2e5cf9
fix(examples): project sheet release from gesture velocity
Oct 4, 2026
155b4a6
chore(integration): test local itinerary candidate
lemone112 Oct 4, 2026
b60a2c2
test(motion): replay reading shelf across browser engines
lemone112 Oct 4, 2026
b7033de
fix(motion): preserve profile and resource proof integrity
lemone112 Oct 4, 2026
c4050bf
fix(motion): resolve review findings in docs and catalog checks
lemone112 Oct 4, 2026
908fdc9
fix(examples): preserve package and browser evidence invariants
lemone112 Oct 4, 2026
336e42e
fix(examples): close sibling review findings
lemone112 Oct 4, 2026
49eff7b
fix(test): build before parallel Vitest execution
lemone112 Oct 4, 2026
6b7f9dc
test(journey): bind browser consumers to one package tuple
Oct 4, 2026
73acc26
test(journey): fence readback to local package artifact
Oct 4, 2026
74a42be
test(resource): verify detached DOM target retention
Oct 4, 2026
70300eb
refactor(profile): отделить файловый адаптер от контракта измерений
lemone112 Oct 4, 2026
721b947
docs(profile): classify measurement guide as how-to
lemone112 Oct 4, 2026
fdaca8e
fix(compositor): preserve live intent during native handoff
lemone112 Oct 4, 2026
4aa2cdb
fix(compositor): сохранить новый live intent при передаче браузеру
lemone112 Oct 4, 2026
b723b20
refactor(motion-value): сократить повторную публикацию цели
lemone112 Oct 4, 2026
1d87146
refactor(stagger): убрать повторный выбор начальной точки
lemone112 Oct 4, 2026
2a9fba0
test(motion): объединить проверку архива и жизненного цикла браузера
lemone112 Oct 4, 2026
9bbb657
docs(profile): integrate reviewed measurement guide
lemone112 Oct 5, 2026
c537d64
docs(changelog): describe live intent preservation
lemone112 Oct 5, 2026
38a3b55
style(example): remove trailing empty line
lemone112 Oct 5, 2026
37159ae
fix: cancel stale MotionValue targets and samples
lemone112 Oct 5, 2026
3e2728a
docs: describe MotionValue rest cancellation
lemone112 Oct 5, 2026
eb45089
docs: перевести контракт управления движением на русский
lemone112 Oct 5, 2026
ac38040
docs(compositor): перевести описание контроллера следования
lemone112 Oct 5, 2026
577293e
fix(animate): освобождение завершённых исполнителей и единый цикл при…
lemone112 Oct 5, 2026
fc2a579
chore: обновить базу объединённых сценариев движения
lemone112 Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ on:
pull_request:
merge_group:
workflow_dispatch:
inputs:
profile_baseline:
description: 'Снять зарегистрированный размерный baseline PROFILE-01'
type: boolean
default: false

permissions:
contents: read
Expand All @@ -21,6 +26,12 @@ env:
COREPACK_DEFAULT_TO_LATEST: "0"

jobs:
# Только явный baseline-запрос: обычный кандидат может менять runtime и
# не обязан совпадать с зарегистрированным старым источником PROFILE-01.
profile-baseline:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.profile_baseline }}
uses: ./.github/workflows/profile-01.yml

verify:
name: typecheck · build · size · package
runs-on: ubuntu-latest
Expand Down Expand Up @@ -180,6 +191,16 @@ jobs:
- name: Fuzz · finiteness gate (virtual-time, seeded LCG, zero-dep)
run: pnpm vitest run --reporter=verbose test/*finiteness-fuzz.test.ts

# Проверяем настоящий C/header/API на зарегистрированном Node, сохраняя
# обычный suite выше на текущем Node24. Это не performance calibration.
- name: Setup registered native-clock Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6
with:
node-version: "24.19.0"

- name: Native CPU clock · actual source and refusal controls
run: node test/fixtures/server-thread-cpu-clock-check.mjs

mutation:
name: driver mutation
runs-on: ubuntu-latest
Expand Down
121 changes: 121 additions & 0 deletions .github/workflows/profile-01.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: PROFILE-01 — проверка размерного протокола

# Размерный вектор вызывается вручную через ci.yml в той же среде, что обычный CI
# публичного репозитория. Время/GPU/энергия этой машины не являются device proof.
on:
workflow_call:

permissions:
contents: read

env:
COREPACK_ENABLE_DOWNLOAD_PROMPT: '0'
COREPACK_DEFAULT_TO_LATEST: '0'
PROFILE_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}

jobs:
probe:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Получить точный источник измерителя
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
fetch-depth: 0

- name: Настроить Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6
with:
node-version: '24'

- name: Активировать закреплённый pnpm
run: |
corepack enable
corepack install --global pnpm@11.11.0

- name: Проверить точную версию протокола
shell: bash
run: |
set -euo pipefail
test "$(git rev-parse HEAD)" = "$PROFILE_SOURCE_SHA"
node --input-type=module <<'NODE'
import { PROFILE_01, preregistrationDigest, verifyPreregistration } from './bench/profile/profile-01-preregistration.mjs';
verifyPreregistration(PROFILE_01);
if (PROFILE_01.candidateSamplesObservedAtRegistration !== false) {
throw new Error('preregistration обязана предшествовать samples');
}
console.log(`preregistration digest: ${preregistrationDigest(PROFILE_01)}`);
NODE

- name: Установить зависимости
run: pnpm install --frozen-lockfile

- name: Измерить прежний размерный вектор
id: probe
shell: bash
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/profile-01-raw"
node bench/profile/probe-profile-01.mjs \
--mode old-vector \
--cells all \
--out "$RUNNER_TEMP/profile-01-raw"

- name: Независимо перепроверить raw-артефакт
if: ${{ !cancelled() && steps.probe.outcome != 'skipped' }}
shell: bash
run: |
set -euo pipefail
for raw in "$RUNNER_TEMP"/profile-01-raw/*.json; do
test -f "$raw"
node bench/profile/validate-profile-01.mjs --raw "$raw"
done

- name: Проверить отказ внешнего validator на подменах настоящего результата
if: ${{ !cancelled() && steps.probe.outcome == 'success' }}
shell: bash
run: |
set -euo pipefail
node --input-type=module <<'NODE'
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const rawDirectory = join(process.env.RUNNER_TEMP, 'profile-01-raw');
const files = readdirSync(rawDirectory).filter(name => name.endsWith('.json'));
assert.equal(files.length, 1);
const original = JSON.parse(readFileSync(join(rawDirectory, files[0]), 'utf8'));
const directory = mkdtempSync(join(tmpdir(), 'profile-replay-mutations-'));
try {
const cases = [
['число', value => { value.costVector.scenarios[0].gzBytes += 1; }, 'scenarios не воспроизводится'],
['identity', value => { value.head = '0'.repeat(40); }, 'baseProof не покрывает'],
['клетки', value => { value.cellsUnproven = []; }, 'неизмеренные клетки потеряны'],
];
for (const [name, mutate, rejection] of cases) {
const value = structuredClone(original);
mutate(value);
const path = join(directory, 'mutated.json');
writeFileSync(path, JSON.stringify(value));
const run = spawnSync(process.execPath, ['bench/profile/validate-profile-01.mjs', '--raw', path],
{ encoding: 'utf8', timeout: 180_000, maxBuffer: 8 * 1024 * 1024 });
assert.equal(run.status, 1, `${name}: ${run.error ?? run.stderr}`);
assert.ok(run.stderr.includes(rejection), `${name}: неверная причина отказа: ${run.stderr}`);
console.log(`${name}: ожидаемый предметный отказ подтверждён`);
}
} finally {
rmSync(directory, { recursive: true, force: true });
}
NODE

- name: Опубликовать raw-артефакт
# Отрицательный результат сохраняется даже после отказа измерителя.
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: profile-01-raw-${{ env.PROFILE_SOURCE_SHA }}
path: ${{ runner.temp }}/profile-01-raw/
retention-days: 90
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ blob-report
.playwright
browser/.artifacts
site/dist
examples/reading-shelf/.artifacts
examples/itinerary-sheet/.artifacts
examples/call-sheet/.artifacts
examples/audio-cue/.artifacts
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@
## [Unreleased]

### Исправлено
- `CompositorSpring`: синхронный callback во время передачи движения композитору
сохраняет новую цель, остановку или уничтожение. Устаревшая передача больше
не подменяет новое состояние живого движения.
- `animate`: завершённые controls освобождают исполнителей, а завершённый
compositor-слот освобождает кривую и live-delegate даже при активном sibling.
Вложенный handoff из getter `requestFrame` сохраняет один кадровый цикл
после частичного host-отказа и в минифицированном npm-пакете.
- `MotionValue` и подписки `./behaviors`: уничтожение освобождает внедрённый
scheduler и callback компонента даже при сохранённых controller/off handles.
Живые подписки сохраняют дедупликацию callback; `stop()` остаётся возобновляемым.
- `MotionValue`: отзыв подписки освобождает callback; старая отписка не удаляет
повторную регистрацию. Callback нового запуска не теряется при перезапуске
из getter или слушателя до возврата прежнего tick.
- `./svelte`: повторная подписка использует текущую регистрацию; отписка и
`destroy()` освобождают её callback, а старый handle не затрагивает преемника.
- `MotionValue` после `destroy()` освобождает пользовательские `spring` и `requestFrame`,
не принимает новые подписки и не продолжает кадр или планирование после уничтожения
из getter или callback `requestFrame`. `CompositorSpring` и follow-контроллер освобождают
исходный `spring`; инертный handoff больше не читает его getters.
- `./vue`: остановка `effectScope` освобождает активные `useMotionValue` и
`useSpring`, включая уже поставленные кадры. Уборка при unmount в Vue 3.0/3.1
и явный `MotionValue.destroy()` вне scope сохранены.
- `./behaviors`: изменение snap-точек или размеров страниц сохраняет текущего
владельца движения. Отмена и уничтожение из подписчика прекращают последующую
доводку, планирование кадров и внешнее уведомление о закрытии.
Expand All @@ -18,6 +40,10 @@
публичные типы не расширены.

### Добавлено
- `CompositorSpring.handoffToCompositor(target)` завершает интерактивную live-фазу
автономным переходом с текущей позиции и скорости. Старый MotionValue становится
инертным после успешного native commit; неподдержанная форма продолжает live-путь.
Исполняемые примеры sheet и pager оставляют выбор snap/page и геометрию компоненту.
- Управляемое присутствие: `createPresenceTransition` связывает одну цель видимости
с группой анимаций, отменяет устаревший уход и освобождает исполнителей при очистке.
Рабочий пример диалога поддерживает повторное открытие и Escape без ручного `done`.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ spring-солвер, кейфреймы, инерция, FLIP) отделена
pnpm add @labpics/motion
```

Node ≥ 22, ESM и CJS, типы в комплекте. Фреймворк для биндинга — optional
Серверная среда — Node ≥ 22; браузерная — ES2022 со встроенным `WeakRef`.
ESM и CJS, типы в комплекте. Фреймворк для биндинга — optional
peer. Git-установка не поддерживается (`dist/` собирается, в репозитории его
нет) — установка из тарбола описана в [справочнике](docs/api.md).

Expand Down Expand Up @@ -111,11 +112,11 @@ pnpm site:preview # отдаёт site/dist на локальном серве
## Карта пакета

Импорт — `@labpics/motion` (ядро) или `@labpics/motion/<субпуть>`.
Корневой экспорт + 43 субпути (входов `exports` в `package.json` — 44):
Корневой экспорт + 44 субпути (входов `exports` в `package.json` — 45):

| Группа | Субпути |
| --- | --- |
| Ядро анимации | `./nano` (WAAPI ≤ 1 КБ), `./animate` (one-liner фасад), `./frame`, `./driver`, `./compositor`, `./compositor/stagger`, `./waapi`, `./auto` |
| Ядро анимации | `./nano` (WAAPI ≤ 1 КБ), `./animate` (one-liner фасад), `./frame`, `./driver`, `./compositor`, `./compositor/stagger`, `./compositor/follow`, `./waapi`, `./auto` |
| Значения и физика | `./value`, `./spring`, `./decay`, `./easing`, `./keyframes`, `./stagger`, `./timeline`, `./presets`, `./tokens`, `./utils` |
| Доменные эффекты | `./bindings`, `./flip`, `./projection`, `./smart`, `./gestures`, `./behaviors`, `./behaviors/reorder`, `./scroll`, `./in-view`, `./presence`, `./svg`, `./svg-morph`, `./a11y`, `./compiler/surface` (приватный executor compiled-поверхности) |
| Биндинги | `./react`, `./preact`, `./vue`, `./svelte`, `./solid`, `./angular`, `./qwik`, `./lit`, `./wc` |
Expand Down Expand Up @@ -200,7 +201,7 @@ springFromDurationBounce(0.35, 0); // восприятие (duration, bounce)
| Раздел | Что внутри |
| --- | --- |
| [Рецепты](docs/recipes.md) | Runnable-интеграции: drag, FLIP, presence, скролл, bottom sheet |
| [Справочник API](docs/api.md) | Все 43 субпути: что даёт каждый вход |
| [Справочник API](docs/api.md) | Все 44 субпути: что даёт каждый вход |
| [Архитектура](docs/architecture.md) | Слои движка, фазовая модель, инварианты, отвергнутые пути |
| [Compositor](docs/compositor.md) | WAAPI-план, ретаргет, хендофф, fallback-матрица, поддержка браузеров |
| [Projection](docs/projection.md) | Вложенный FLIP: дерево узлов, C¹-перехваты, не-цели v1 |
Expand Down
Loading
Loading